/*
 * Created by Spear (http://www.codernet.es), spear@codernet.es
 * Coordinator of XNA Community (http://www.codeplex.com/XNACommunity)
 * 
 */

using System;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Content.Pipeline;
using System.Globalization;

namespace XMasContent
{
  public class CMD5Parser
  {
    class SElements
    {
      public enum TElementType
      {
        STRING_VALUE,
        NUMERIC_VALUE,
        VECTOR2_VALUE,
        VECTOR3_VALUE,
        EMPTY_VALUE
      };
      public string value_str;
      public float value_num;
      public Vector2 value_vec2;
      public Vector3 value_vec3;
      public TElementType value_type;
    };
    class SKeyParse
    {
      public string key_name;
      public int seek_elem;
      public List<SElements> elements;
    };
    List<SKeyParse> keys_list = new List<SKeyParse>();
    string[] buffer;
    int seek_buffer = 0;
    int seek = -1;

    public CMD5Parser(string filename)
    {
      char[] split_chars = { ' ', '\r', '\n', '\t' };
      StreamReader reader = new StreamReader(filename);
      string b = reader.ReadToEnd();
      buffer = b.Split(split_chars);
      reader.Close(); 
      while (seek_buffer < buffer.Length)
      {
        if (buffer[seek_buffer].Length > 0)
        {
          if (buffer[seek_buffer][0] == '/')
          {
            parserComment();
          }
          else if (buffer[seek_buffer][0] == '"') //found text
          {
            parserString();
          }
          else if (Char.IsNumber(buffer[seek_buffer][0]) || buffer[seek_buffer][0] == '-') //found int
          {
            parserNumeric();
          }
          else if (buffer[seek_buffer][0] == '(') //found vector
          {
            parserVector();
          }
          else if (Char.IsLetter(buffer[seek_buffer][0])) //found key
          {
            parserKey();
          }
          else
          {
            ++seek_buffer;
          }
        }
        else
        {
          ++seek_buffer;
        }
      }
    }

    private void parserComment()
    {
      ++seek_buffer;
      while ( seek_buffer < buffer.Length && (buffer[seek_buffer].Length != 0 || buffer[seek_buffer + 1].Length != 0))
      {
        ++seek_buffer;
      }
      seek_buffer += 2;
    }

    private void parserVector()
    {
      if (buffer[seek_buffer + 3][0] == ')')
      {
        Vector2 vec = new Vector2();
        ++seek_buffer;
        vec.X = float.Parse(buffer[seek_buffer], CultureInfo.InvariantCulture);
        ++seek_buffer;
        vec.Y = float.Parse(buffer[seek_buffer], CultureInfo.InvariantCulture);
        seek_buffer += 2;
        int last_key = keys_list.Count - 1;
        if (last_key >= 0)
        {
          SElements item = new SElements();
          item.value_vec2 = vec;
          item.value_type = SElements.TElementType.VECTOR2_VALUE;
          keys_list[last_key].elements.Add(item);
        }
      }
      else
      {
        Vector3 vec = new Vector3();
        ++seek_buffer;
        vec.X = float.Parse(buffer[seek_buffer], CultureInfo.InvariantCulture);
        ++seek_buffer;
        vec.Y = float.Parse(buffer[seek_buffer], CultureInfo.InvariantCulture);
        ++seek_buffer;
        vec.Z = float.Parse(buffer[seek_buffer], CultureInfo.InvariantCulture);
        seek_buffer += 2;
        int last_key = keys_list.Count - 1;
        if (last_key >= 0)
        {
          SElements item = new SElements();
          item.value_vec3 = vec;
          item.value_type = SElements.TElementType.VECTOR3_VALUE;
          keys_list[last_key].elements.Add(item);
        }
      }
    }

    private void parserNumeric()
    {
      int last_key = keys_list.Count - 1;
      if (last_key >= 0)
      {
        SElements item = new SElements();
        item.value_num = float.Parse(buffer[seek_buffer], CultureInfo.InvariantCulture);
        item.value_type = SElements.TElementType.NUMERIC_VALUE;
        keys_list[last_key].elements.Add(item);
      }
      ++seek_buffer;
    }

    private void parserKey()
    {
      SKeyParse item = new SKeyParse();
      item.elements = new List<SElements>();
      item.seek_elem = 0;
      item.key_name = buffer[seek_buffer].ToLower();
      keys_list.Add(item);
      ++seek_buffer;
    }
    private void parserString()
    {
      string text = "";
      //end to string is other '"' character
      if (buffer[seek_buffer].Substring(1).IndexOf('"') >= 0)
      {
        int len = buffer[seek_buffer].Length;
        text = buffer[seek_buffer].Substring(1, len - 2);
      }
      else  //search the end to string (character '"')
      {
        text = buffer[seek_buffer].Substring(1);
        int start_seek_buffer = seek_buffer;
        ++seek_buffer;
        while ( buffer[seek_buffer].IndexOf('"') < 0 )
        {
          text = text + " " + buffer[seek_buffer];
          ++seek_buffer;
        }
        int len = buffer[seek_buffer].Length;
        text = text + " " + buffer[seek_buffer].Substring(0, len - 2);
      }
      int last_key = keys_list.Count - 1;
      if (last_key >= 0)
      {
        SElements item = new SElements();
        item.value_str = text;
        item.value_type = SElements.TElementType.STRING_VALUE;
        keys_list[last_key].elements.Add(item);
      }
      ++seek_buffer;
    }

    public void seekToBegin()
    {
      seek = 0;
    }

    public void seekToNextKey(string key_name) {
      key_name = key_name.ToLower();
      for (++seek; seek < keys_list.Count && keys_list[seek].key_name != key_name; ++seek);
    }

    public Vector3 getVector3() {
      Vector3 vec = Vector3.Zero;
      if (seek < keys_list.Count)
      {
        int seek_elem = keys_list[seek].seek_elem;
        if (seek_elem < keys_list[seek].elements.Count)
        {
          if (keys_list[seek].elements[seek_elem].value_type == SElements.TElementType.VECTOR3_VALUE)
          {
            vec = keys_list[seek].elements[seek_elem].value_vec3;
          }
          else
          {
            //Error, this element isn't a Vector
            warn("Error!, this elemnt isn't a Vector");
          }
          keys_list[seek].seek_elem = keys_list[seek].seek_elem + 1;
        }
      }
      return vec;
    }

    public Vector2 getVector2()
    {
      Vector2 vec = Vector2.Zero;
      if (seek < keys_list.Count)
      {
        int seek_elem = keys_list[seek].seek_elem;
        if (seek_elem < keys_list[seek].elements.Count)
        {
          if (keys_list[seek].elements[seek_elem].value_type == SElements.TElementType.VECTOR2_VALUE)
          {
            vec = keys_list[seek].elements[seek_elem].value_vec2;
          }
          else
          {
            //Error, this element isn't a Vector
            warn("Error!, this elemnt isn't a Vector");
          }
          keys_list[seek].seek_elem = keys_list[seek].seek_elem + 1;
        }
      }
      return vec;
    }

    public float getNumber()
    {
      float value = 0;
      if (seek < keys_list.Count)
      {
        int seek_elem = keys_list[seek].seek_elem;
        if (seek_elem < keys_list[seek].elements.Count)
        {
          if (keys_list[seek].elements[seek_elem].value_type == SElements.TElementType.NUMERIC_VALUE)
          {
            value = keys_list[seek].elements[seek_elem].value_num;
          }
          else
          {
            //Error, this element isn't a Int
            warn("Error!, this element isn't a Number");
          }
          keys_list[seek].seek_elem = keys_list[seek].seek_elem + 1;
        }
      }
      return value;
    }

    public string getString()
    {
      string value = "";
      if (seek < keys_list.Count)
      {
        int seek_elem = keys_list[seek].seek_elem;
        if (seek_elem < keys_list[seek].elements.Count)
        {
          if (keys_list[seek].elements[seek_elem].value_type == SElements.TElementType.STRING_VALUE)
          {
            value = keys_list[seek].elements[seek_elem].value_str;
          }
          else
          {
            //Error, this element isn't a String
            warn("Error!, this element isn't a String");
          }
          keys_list[seek].seek_elem = keys_list[seek].seek_elem + 1;
        }
      }
      return value;
    }

    public bool isEOFKey()
    {
      int seek_elem = keys_list[seek].seek_elem;
      if (seek_elem < keys_list[seek].elements.Count)
      {
        return false;
      }
      return true;
    }

    private void warn(string error) {
      //Debug.Assert(error + "\n");
    }
  }
}
